// ignore_for_file: constant_identifier_names

import 'package:flutter_base_plugin/flutter_base_plugin.dart';

enum Method {
  GET,
  POST,
  DELETE,
  PUT,
  HEAD,
  PATCH,
}

///使用MethodValues[Method.POST]
const MethodValues = {
  Method.GET: "GET",
  Method.POST: "POST",
  Method.PUT: "PUT",
  Method.HEAD: "HEAD",
  Method.DELETE: "DELETE",
  Method.PATCH: "PATCH",
};

///网络请求管理单例
class XApi {
  static const _tag = "x_api";
  static const contentTypeJson = Headers.jsonContentType;
  static const contentTypeForm = Headers.formUrlEncodedContentType;
  static const downLoadReceiveTimeout = 60 * 1000;

  static final XApi _instance = XApi._();
  static late IHttpConfig _httpConfig;
  static late IHttpBean _httpBean;

  static late Dio _dio;
  final List<CancelToken> _cancelTokenList = [];

  ///初始化网络相关参数
  static void init(IHttpConfig httpConfig, IHttpBean httpBean) {
    HttpCode.successCodeList.addAll(httpBean.configHttpSuccessCodeList());

    _httpConfig = httpConfig;
    _httpBean = httpBean;

    _dio = Dio(_httpConfig.configBaseOptions());

    ///拦截器队列的执行顺序是FIFO
    ///添加自定义拦截器
    if (_httpConfig.configInterceptors() != null) {
      _dio.interceptors.addAll(_httpConfig.configInterceptors()!);
    }

    _dio.interceptors.add(LogsInterceptors());

    // (_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
    //     (client) {
    //   client.badCertificateCallback =
    //       (X509Certificate cert, String host, int port) {
    //     ///true：忽略证书校验
    //     return _httpConfig.configHttps(cert, host, port);
    //   };
    // };
  }

  factory XApi() {
    return _instance;
  }

  XApi._();

  ///下载文件
  /// [url] 下载地址
  /// [savePath] 本地保存的地址
  /// [receiveTimeout] 下载文件超时时间ms
  /// [options] 针对当前请求的配置选项
  /// [onReceiveProgress] 下载进度
  /// return 是否下载成功
  Future<bool> downloadFile(
    String url,
    String savePath, {
    int? receiveTimeout = downLoadReceiveTimeout,
    Options? options,
    CancelToken? cancelToken,
    ProgressCallback? onReceiveProgress,
  }) async {
    LogUtils.i(_tag, 'downloadFile() url:$url  savePath:$savePath');
    bool isSuccess;

    ///添加CancelToken,用于取消请求
    cancelToken ??= CancelToken();
    _cancelTokenList.add(cancelToken);

    ///设置超时时间
    if (receiveTimeout != null) {
      options ??= Options();
      options.receiveTimeout = receiveTimeout;
    }

    try {
      await _dio.download(url, savePath,
          cancelToken: cancelToken,
          onReceiveProgress: onReceiveProgress,
          options: options);
      isSuccess = true;
      LogUtils.i(_tag, 'downloadFile() success, url:$url  savePath:$savePath');
    } catch (e) {
      LogUtils.e(_tag, 'downloadFile fail:$e');
      isSuccess = false;
    }

    ///请求完成移除cancelToken
    _cancelTokenList.remove(cancelToken);
    return isSuccess;
  }

  ///发起请求,响应data为单个对象
  ///[url] 请求url，如果 `url` 以 "http(s)"开始, 则 `baseURL` 会被忽略； 否则,将会和baseUrl拼接出完整的的url.
  ///[params] 请求参数
  ///[header] 请求头
  ///[method] 请求方法，优先级最高
  ///[option] 针对当前请求的配置选项，优先级次高
  ///[isShowLoading] 是否显示加载弹窗，默认true
  ///[loadingText] 加载提示，默认“loading”
  ///[isCancelableDialog] 加载中能否关闭加载弹窗，默认false
  ///[isShowFailToast] 请求失败时是否自动显示toast提示错误，默认true
  ///[onSuccess] 请求成功回调
  ///[onError] 请求失败回调
  ///[onComplete] 请求完成回调，在onSuccess或onError方法后面调用
  Future request<T>(String url, Method method,
      {Map<String, dynamic>? params,
      Map<String, dynamic>? header,
      // Options? option,
      bool? isShowLoading,
      String? loadingText,
      bool? isCancelableDialog,
      bool? isShowFailToast,
      bool? isCheckNetwork,
      CancelToken? cancelToken,
      Function(T?)? onSuccess,
      Function(HttpErrorBean)? onError,
      Function()? onComplete}) async {
    return _commonRequest<T>(url, method, false,
        params: params,
        header: header,
        // option: option,
        isShowLoading: isShowLoading,
        loadingText: loadingText,
        isCancelableDialog: isCancelableDialog,
        isShowFailToast: isShowFailToast,
        cancelToken: cancelToken,
        onSuccessObjCallback: onSuccess,
        onErrorCallback: onError,
        onCompleteCallback: onComplete);
  }

  ///发起请求,响应data为List
  ///[url]请求url，如果 `url` 以 "http(s)"开始, 则 `baseURL` 会被忽略； 否则,将会和baseUrl拼接出完整的的url.
  ///[params] 请求参数
  ///[header] 请求头
  ///[method] 请求方法，优先级最高
  ///[option] 针对当前请求的配置选项，优先级次高
  ///[isShowLoading] 是否显示加载弹窗，默认true
  ///[loadingText] 加载提示，默认“loading”
  ///[isCancelableDialog] 加载中能否关闭加载弹窗，默认false,关闭弹窗同时取消请求
  ///[isShowFailToast] 请求失败时是否自动显示toast提示错误，默认true
  ///[onSuccess] 请求成功回调
  ///[onError] 请求失败回调
  ///[onComplete] 请求完成回调，在onSuccess或onError方法后面调用
  Future requestList<T>(String url, Method method,
      {Map<String, dynamic>? params,
      Map<String, dynamic>? header,
      // Options? option,
      bool? isShowLoading,
      String? loadingText,
      bool? isCancelableDialog,
      bool? isShowFailToast,
      CancelToken? cancelToken,
      Function(List<T>?)? onSuccess,
      Function(HttpErrorBean)? onError,
      Function()? onComplete}) async {
    return _commonRequest<T>(url, method, true,
        params: params,
        header: header,
        // option: option,
        isShowLoading: isShowLoading,
        loadingText: loadingText,
        isCancelableDialog: isCancelableDialog,
        isShowFailToast: isShowFailToast,
        cancelToken: cancelToken,
        onSuccessListCallback: onSuccess,
        onErrorCallback: onError,
        onCompleteCallback: onComplete);
  }

  ///通用http请求
  ///[url] 请求url，如果 `url` 以 "http(s)"开始, 则 `baseURL` 会被忽略； 否则,将会和baseUrl拼接出完整的的url.
  ///[params] 请求参数,可为空
  ///[isResultList] 返回的data是否是List类型
  ///[header] 请求头
  ///[method] 请求方法，优先级最高
  ///[option] 针对当前请求的配置选项，优先级次高
  ///[isShowLoading] 是否显示加载弹窗
  ///[loadingText] 加载提示
  ///[isCancelableDialog] 加载中能否关闭加载弹窗
  ///[isShowFailToast] 请求失败时是否自动显示toast提示错误
  ///[onSuccessListCallback] 请求List成功回调
  ///[onSuccessObjCallback] 请求单个对象成功回调
  ///[onErrorCallback] 请求失败回调
  ///[onComplete] 请求完成回调，在onSuccess或onError方法后面调用
  Future _commonRequest<T>(String url, Method method, bool isResultList,
      {Map<String, dynamic>? params,
      Map<String, dynamic>? header,
      // Options? option,
      bool? isShowLoading,
      String? loadingText,
      bool? isCancelableDialog,
      bool? isShowFailToast,
      CancelToken? cancelToken,
      Function(List<T>?)? onSuccessListCallback,
      Function(T?)? onSuccessObjCallback,
      Function(HttpErrorBean)? onErrorCallback,
      Function()? onCompleteCallback}) async {
    ///设置默认值
    isShowLoading ??= _httpConfig.isShowLoading();
    loadingText ??= _httpConfig.configLoadingText();
    isCancelableDialog ??= _httpConfig.isCancelableDialog();
    isShowFailToast ??= _httpConfig.isShowFailToast();

    ///判断网络连接
    ConnectivityResult connResult = await Connectivity().checkConnectivity();
    if (connResult == ConnectivityResult.none) {
      _onRespErrorCallback(
        isShowFailToast,
        onErrorCallback,
        HttpErrorBean(code: HttpCode.networkError, message: '无网络连接，请检查网络设置'),
      );

      ///请求完成回调
      // onCompleteCallback?.call();
      return;
    }

    Options option = Options();
    option.headers = _httpConfig.configOptionsHeaders();

    ///添加baseUrl
    ///baseUrl优先级：形参url>_httpConfig.getBaseUrl>_httpConfig.configBaseOptions
    if (!url.startsWith(Constants.httpStartWith)) {
      String? baseUrl = _httpConfig.getBaseUrl(url);
      if (baseUrl.isNotEmpty) {
        url = baseUrl + url;
      }
    }

    params ??= {};

    ///添加CancelToken,用于取消请求
    cancelToken ??= CancelToken();
    _cancelTokenList.add(cancelToken);

    ///处理请求头
    if (header != null) {
      option.headers ??= {};
      option.headers!.addAll(header);
    }

    ///设置请求方法
    option.method = MethodValues[method];

    ///显示加载框
    if (isShowLoading) {
      _httpConfig.showLoading(url, cancelToken.hashCode, cancelToken,
          loadingText, isCancelableDialog);
    }
    try {
      Response response;
      response = await _dio.request(url,
          data: params,
          queryParameters: params,
          options: option,
          cancelToken: cancelToken);
      HttpResultBean<T> resultBean =
          await _parseJsonToObject<T>(url, response, isResultList);
      if (resultBean.isSuccess()) {
        _onRespSuccessCallback<T>(
            resultBean, onSuccessObjCallback, onSuccessListCallback);
      } else {
        _onRespErrorCallback(isShowFailToast, onErrorCallback,
            HttpErrorBean(code: resultBean.code, message: resultBean.message));
      }
    } on DioError catch (e) {
      _onRespErrorCallback(
          isShowFailToast, onErrorCallback, ErrorHandle.createErrorEntity(e));
    } catch (exception) {
      LogUtils.e(_tag, ' 异常:${exception.toString()}');
      _onRespErrorCallback(
        isShowFailToast,
        onErrorCallback,
        HttpErrorBean(code: HttpCode.fail, message: exception.toString()),
      );
    } finally {
      ///请求完成，隐藏加载框
      if (isShowLoading) {
        _httpConfig.hideLoading(
            url, cancelToken.hashCode, cancelToken.isCancelled);
      }

      ///请求完成移除cancelToken
      if (_cancelTokenList.contains(cancelToken)) {
        _cancelTokenList.remove(cancelToken);
      }

      ///请求完成回调
      onCompleteCallback?.call();
    }
  }

  ///http响应json解析为对象
  ///[response] http 响应的对象
  ///[isRespListData] http响应的数据是否是List数据结构
  Future<HttpResultBean<T>> _parseJsonToObject<T>(
      String url, Response response, bool isRespListData) async {
    HttpResultBean<T> resultBean = HttpResultBean();
    resultBean.isRespListData = isRespListData;
    if (response.data == null) {
      resultBean.code = HttpCode.unKnowError;
      resultBean.message = 'response is null';
      return resultBean;
    } else {
      resultBean.code = response.data[_httpBean.configCode()];
      if (resultBean.isSuccess()) {
        if (!isRespListData) {
          resultBean.data = _httpBean
              .parseJsonToObject<T>(response.data[_httpBean.configData()]);
        } else {
          List<T> mData = [];
          if (response.data[_httpBean.configList()] != null) {
            //遍历data并转换为我们传进来的类型
            (response.data[_httpBean.configData()] as List).forEach((v) {
              mData.add(_httpBean.parseJsonToObject<T>(v));
            });
          }
          resultBean.dataList = mData;
        }
      } else {
        resultBean.code = response.data[_httpBean.configCode()];
        resultBean.message = response.data[_httpBean.configMsg()];
      }
    }
    return resultBean;
  }

  ///http响应成功回调
  void _onRespSuccessCallback<T>(
      HttpResultBean<T> resultBean,
      Function(T?)? onSuccessObjCallback,
      Function(List<T>?)? onSuccessListCallback) {
    try {
      if (resultBean.isRespListData) {
        onSuccessListCallback?.call(resultBean.dataList);
      } else {
        onSuccessObjCallback?.call(resultBean.data);
      }
    } catch (e) {
      LogUtils.e(_tag, '_onRespSuccessCallback() 方法回调异常:$e}');
    }
  }

  ///http响应错误回调
  Future<void> _onRespErrorCallback(bool isShowFailToast,
      Function(HttpErrorBean)? onErrorCallback, HttpErrorBean errorBean) async {
    if (isShowFailToast && HttpCode.cancel != errorBean.code) {
      ///主动取消请求时不显示错误信息
      _httpConfig.showFailToast(errorBean);
    }

    try {
      onErrorCallback?.call(errorBean);
    } catch (e) {
      LogUtils.e(_tag, '_onRespErrorCallback() 方法回调异常:$e}');
    }
  }

  ///取消所有请求
  void cancelAll() {
    // LogUtils.i(_tag, 'cancelAll:${_cancelTokenList.length}');
    _cancelTokenList.forEach((cancelToken) {
      cancel(cancelToken);
    });
    _cancelTokenList.clear();
  }

  ///取消指定的请求
  void cancel(CancelToken? cancelToken) {
    if (cancelToken != null && !cancelToken.isCancelled) {
      cancelToken.cancel();
    }
  }

  ///取消指定的请求
  void cancelList(List<CancelToken>? cancelTokenList) {
    // LogUtils.i(_tag, 'cancelList:${cancelTokenList?.length}');
    cancelTokenList?.forEach((cancelToken) {
      cancel(cancelToken);
    });
  }
}
